package com.hmall.search.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.common.utils.BeanUtils;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.domain.vo.PageVO;
import com.hmall.search.service.ISearchService;
import lombok.RequiredArgsConstructor;
import org.apache.http.HttpHost;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class SearchServiceImpl implements ISearchService {

    //索引库名称
    private static final String INDEX_NAME = "items";
    private  final ItemClient itemClient;
    private RestHighLevelClient client;

//    创建连接
     @PostConstruct
//    @BeforeEach
    public void init() {
        client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("192.168.12.168", 9200, "http")
                )
        );
    }
    @Override
    public void saveItemById(Long itemId) {
        //1、根据商品id查询商品信息
        ItemDTO item = itemClient.queryItemById(itemId);
        if (item != null){
            //2、转换为itemDoc json字符串
            ItemDoc itemDoc = BeanUtils.copyProperties(item, ItemDoc.class);
            String json = JSONUtil.toJsonStr(itemDoc);
            //3创建请求
//            IndexRequest request = new IndexRequest(INDEX_NAME).id(itemDoc.getId().toString());
            IndexRequest request = new IndexRequest(INDEX_NAME).id(itemId.toString());
            request.source(json, XContentType.JSON);
            //4发送请求
            try {
                client.index(request, RequestOptions.DEFAULT);
            } catch (IOException e) {
                throw new RuntimeException("更新es中商品失败！参数:" + itemId, e);
            }
        }
    }

    @Override
    public void deleteItemById(Long itemId) {
        //1、创建删除请求
        DeleteRequest deleteRequest = new DeleteRequest(INDEX_NAME, itemId.toString());
        //2、发送请求
        try {
            client.delete(deleteRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException("删除es中商品失败！参数:" + itemId, e);
        }
    }

    /**
     * 搜索商品
     * @param query
     * @return
     */
    @Override
    public PageVO<ItemDoc> search(ItemPageQuery query) {
        try {
//            // 创建查询请求
            SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
            // 设置请求对象参数(搜索关键字、过滤、分页排序、高亮)
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();//因为设计到多种搜索条件，所以使用boolQueryBuilder
            //设置搜索关键字为高亮显示
            boolean isHighlight = false;
            if (StrUtil.isNotBlank(query.getKey())){
                boolQueryBuilder.must(QueryBuilders.matchQuery("name", query.getKey()));
                //说明房户有输入搜索的关键字;需要对关键字进行高亮显示
                isHighlight = true;
            }
            //搜索关键字
            if(StrUtil.isNotBlank(query.getKey())){
                boolQueryBuilder.must(QueryBuilders.matchQuery("name", query.getKey()));
            }
            //搜索分类过滤
            if(StrUtil.isNotBlank(query.getCategory())){
                boolQueryBuilder.filter(QueryBuilders.termQuery("category", query.getCategory()));
            }
            //根据品牌过滤
            if(StrUtil.isNotBlank(query.getBrand())){
                boolQueryBuilder.filter(QueryBuilders.termQuery("brand", query.getBrand()));
            }
            //根据价格区间进行过滤
            //分别进行判断,最大价格和最小价格
            if (query.getMinPrice() != null){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
            }
            if (query.getMaxPrice() != null){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
            }
            //设置排序
            if (StrUtil.isNotBlank(query.getSortBy())){
                //设置排序
                searchRequest.source().sort(query.getSortBy(), query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
            }
            else {//因为有广告，所以需要按照广告进行加权
                //如果没有设置排序，则默认按照更新时间进行降序排序
                searchRequest.source().sort("updateTime", SortOrder.DESC);
            }
            //设置高亮显示
            if (isHighlight){
                searchRequest.source().highlighter(SearchSourceBuilder.highlight()
                        .field("name")
                        .preTags("<font style='color:red'>")
                        .postTags("</font>")
                );
            }
            //根据算法函数，按照广告进行加权算分
            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                    boolQueryBuilder,
                    new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                    QueryBuilders.matchQuery("isAD", true),
                                    ScoreFunctionBuilders.weightFactorFunction(100000)
                            )
                    }
            ).boostMode(CombineFunction.MULTIPLY);
            searchRequest.source().query(functionScoreQueryBuilder);
            //设置分页
            int pageNo = query.getPageNo() == null ? ItemPageQuery.DEFAULT_PAGE_NUM : query.getPageNo();
            int PageSize = query.getPageSize() == null ? ItemPageQuery.DEFAULT_PAGE_SIZE : query.getPageSize();
            searchRequest.source().from((pageNo - 1) * PageSize).size(PageSize);
            //保存到query中进行发送请求
            searchRequest.source().query(boolQueryBuilder);
            //发送请求
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
            //解析响应结果
            PageVO<ItemDoc> pageVO = new PageVO<>();
            SearchHits hits = search.getHits();
            //设置分页的总记录数
            long total = hits.getTotalHits().value;
            pageVO.setTotal(total);
            //设置分页的总页数
            pageVO.setPages(total % PageSize == 0 ? total / PageSize : total / PageSize + 1);
            //设置分页的列表数据
            List<ItemDoc> itemDoclist = new ArrayList<>();
            //从hits中获取数据
            SearchHit[] hitsHits = hits.getHits();
            //遍历并将数组内的数据添加到itemDoclist中并转换成itemDoc对象
            for (SearchHit hit : hitsHits) {
                //获取数据
                String sourceAsString = hit.getSourceAsString();
                //将数据转换成itemDoc对象
                ItemDoc itemDoc = JSONUtil.toBean(sourceAsString, ItemDoc.class);
                //设置高亮数据
                if (isHighlight){
                    //获取高亮处理高亮标题
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    if (highlightFields != null && highlightFields.containsKey("name")){
                        itemDoc.setName(highlightFields.get("name").fragments()[0].toString());
                    }
                }
                itemDoclist.add(itemDoc);
            }
            //设置分页的列表数据
            pageVO.setList(itemDoclist);
            return pageVO;
//            return null;
        } catch (IOException e) {
            throw new RuntimeException("搜索es中的商品失败!"+e.getMessage());
        }
    }

    /**
     * 获取过滤条件
     * @param query
     * @return
     */
    @Override
    public Map<String, List<String>> filters(ItemPageQuery query) {
        try {
//            // 创建查询请求
            SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
            // 设置请求对象参数
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();//因为设计到多种搜索条件，所以使用boolQueryBuilder
            //搜索关键字
            if(StrUtil.isNotBlank(query.getKey())){
                boolQueryBuilder.must(QueryBuilders.matchQuery("name", query.getKey()));
            }
            //搜索分类过滤
            boolean isCategory = true;
            if(StrUtil.isNotBlank(query.getCategory())){
                boolQueryBuilder.filter(QueryBuilders.termQuery("category", query.getCategory()));
                isCategory = false;
            }
            //根据品牌过滤
            boolean isBrand = true;
            if(StrUtil.isNotBlank(query.getBrand())){
                boolQueryBuilder.filter(QueryBuilders.termQuery("brand", query.getBrand()));
                isBrand = false;
            }
            //根据价格区间进行过滤
            //分别进行判断,最大价格和最小价格
            if (query.getMinPrice() != null){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(query.getMinPrice()));
            }
            if (query.getMaxPrice() != null){
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(query.getMaxPrice()));
            }
            //只需要根据分类、品牌分组的结果:并不需要返回文档(商品数据)
            searchRequest.source().size(0);
            //保存到query中进行发送请求
            searchRequest.source().query(boolQueryBuilder);
            //设置分组,分类聚合
            if (isCategory){
                searchRequest.source().aggregation(AggregationBuilders.terms("categoryAgg").field("category").size(20));
            }
            //设置分组,品牌聚合
            if (isBrand){
                searchRequest.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(20));
            }
            //发送请求
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
            //解析响应结果
            Map<String, List<String>> resultMap = new HashMap<>();
            Aggregations aggregations = search.getAggregations();
            //处理分类分组结果
            if (aggregations != null){
                Terms categoryAgg = aggregations.get("categoryAgg");
                if (categoryAgg != null){
                    //获取分类分组结果
                    List<? extends Terms.Bucket> buckets = categoryAgg.getBuckets();
                    //创建一个list集合,用于存储分类分组结果
                    ArrayList<String> categoryList  = new ArrayList<>(buckets.size());
                    for (Terms.Bucket bucket : buckets){
                        categoryList.add(bucket.getKeyAsString());
                    }
                    resultMap.put("category", categoryList);
                }
            }
            //处理品牌分组结果
            if (aggregations != null){
                Terms brandAgg = aggregations.get("brandAgg");
                if (brandAgg != null) {
                    List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
                    ArrayList<String> brandList  = new ArrayList<>(buckets.size());
                    for (Terms.Bucket bucket : buckets){
                        brandList.add(bucket.getKeyAsString());
                    }
                    resultMap.put("brand", brandList);
                }
            }
            return resultMap;
        } catch (IOException e) {
            throw new RuntimeException("搜索es中的商品失败!"+e.getMessage());
        }
    }

    //测试关闭连接
    @AfterEach
    public void close() throws Exception {
        client.close();
    }
}
